/*
 * Copyright (c) 2020-2099 the original author or authors. All rights reserve.
 */
package org.tianyun.cloud.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.json.JsonMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * JSON工具类
 *
 * @auther ebert_chan
 */
public class JsonUtils {

    private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtils.class);

    private static final Map<PropertyNamingStrategy, Json> INSTANCE = new HashMap<>();

    private JsonUtils() {
    }

    /**
     * 使用下划线命名策略
     * <p>
     * 序列化：<br/>
     * jsonUtils ==> json_utils <br/>
     * json_utils ==> json_utils
     * </p>
     *
     * <p>
     * 反序列化：<br/>
     * json_utils ==> jsonUtils <br/>
     * jsonUtils ==> jsonUtils <br/>
     * </p>
     *
     * @return
     * @auther ebert_chan
     */
    public static Json snakeCase() {
        return of(PropertyNamingStrategy.SNAKE_CASE);
    }

    /**
     * 使用驼峰命名策略
     * <p>
     * 序列化：<br/>
     * jsonUtils ==> jsonUtils <br/>
     * json_utils ==> json_utils
     * </p>
     *
     * <p>
     * 反序列化：<br/>
     * jsonUtils ==> jsonUtils <br/>
     * json_utils ==> json_utils
     * </p>
     *
     * @return
     * @auther ebert_chan
     */
    public static Json lowerCamelCase() {
        return of(PropertyNamingStrategy.LOWER_CAMEL_CASE);
    }

    /**
     * 根据指定序列化策略获取Json对象
     *
     * @param strategy
     * @return
     * @auther ebert_chan
     */
    public static Json of(PropertyNamingStrategy strategy) {
        Objects.requireNonNull(strategy);
        if (!INSTANCE.containsKey(strategy)) {
            synchronized (INSTANCE) {
                if (!INSTANCE.containsKey(strategy)) {
                    INSTANCE.put(strategy, new Json(JsonInclude.Include.NON_NULL, strategy, false));
                }
            }
        }
        return INSTANCE.get(strategy);
    }

    /**
     * 序列化对象为String字符串
     *
     * @param object
     * @return
     * @auther ebert_chan
     */
    public static String toJsonString(Object object) {
        return lowerCamelCase().toJsonString(object);
    }

    /**
     * 检查类型是否支持序列化
     *
     * @param type
     * @return
     * @auther ebert_chan
     */
    public static boolean canSerialize(Class<?> type) {
        return lowerCamelCase().canSerialize(type);
    }

    /**
     * JSON字符串反序列化为指定对象
     *
     * @param content
     * @param type
     * @param parameterClasses
     * @return
     * @auther ebert_chan
     */
    public static <T> T parse(String content, Class<?> type, Class<?>... parameterClasses) {
        return lowerCamelCase().parse(content, type, parameterClasses);
    }

    /**
     * JSON字符串反序列化为指定对象
     *
     * @param content
     * @param typeReference
     * @return
     * @auther ebert_chan
     */
    public static <T> T parse(String content, TypeReference<T> typeReference) {
        return lowerCamelCase().parse(content, typeReference);
    }

    /**
     * Json序列化、反序列化操作
     *
     * @auther ebert_chan
     */
    public static class Json {

        private JsonMapper objectMapper;

        public Json(JsonInclude.Include include, PropertyNamingStrategy propertyNamingStrategy, boolean failOnUnknownProperties) {
            objectMapper = new JsonMapper.Builder(new JsonMapper()).serializationInclusion(include)
                    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, failOnUnknownProperties).build();
            objectMapper.setPropertyNamingStrategy(propertyNamingStrategy);
        }

        /**
         * 序列化对象为String字符串
         *
         * @param object
         * @return
         * @auther ebert_chan
         */
        public String toJsonString(Object object) {
            String json = null;
            if (object == null) {
                return json;
            }
            try {
                json = objectMapper.writeValueAsString(object);
            } catch (JsonProcessingException e) {
                LOGGER.error("JSON序列化异常", e);
            }
            return json;
        }

        /**
         * 检查类型是否支持序列化
         *
         * @param type
         * @return
         * @auther ebert_chan
         */
        public boolean canSerialize(Class<?> type) {
            return objectMapper.canSerialize(type);
        }

        /**
         * JSON字符串反序列化为指定对象
         *
         * @param content
         * @param type
         * @param parameterClasses
         * @return
         * @auther ebert_chan
         */
        @SuppressWarnings("unchecked")
        public <T> T parse(String content, Class<?> type, Class<?>... parameterClasses) {
            if (content == null || content.length() == 0) {
                return null;
            }

            T object = null;
            try {
                if (Map.class.isAssignableFrom(type)) {
                    object = (T) objectMapper.readValue(content, type);
                } else {
                    JavaType javaType = objectMapper.getTypeFactory().constructParametricType(type, parameterClasses);
                    object = objectMapper.readValue(content, javaType);
                }

            } catch (JsonParseException e) {
                LOGGER.error("JSON解析异常", e);
            } catch (JsonMappingException e) {
                LOGGER.error("JSON映射异常", e);
            } catch (JsonProcessingException e) {
                LOGGER.error("JSON反序列化异常", e);
            }

            return object;
        }

        /**
         * JSON字符串反序列化为指定对象
         *
         * @param content
         * @param typeReference
         * @return
         * @auther ebert_chan
         */
        @SuppressWarnings("unchecked")
        public <T> T parse(String content, TypeReference<T> typeReference) {
            if (content == null || content.length() == 0) {
                return null;
            }

            T object = null;
            try {
                object = objectMapper.readValue(content, typeReference);
            } catch (JsonParseException e) {
                LOGGER.error("JSON解析异常", e);
            } catch (JsonMappingException e) {
                LOGGER.error("JSON映射异常", e);
            } catch (JsonProcessingException e) {
                LOGGER.error("JSON反序列化异常", e);
            }

            return object;
        }

    }

}
